﻿/*
 * @author: wizardc
 */

using System.Collections.Generic;
using System.IO;
using Dou.Utils;
using DouEditor.Extend;
using DouEditor.Utils;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Demo.WorldMap
{
    /// <summary>
    /// 场景静态渲染数据导出窗口
    /// 美术场景规范：
    /// 1. 数据取自场景，支持从多场景中获取数据；
    /// 2. 最小单位的对象（GPU是挂载了LODGroup组件的对象，预制是单个对象），都必须是预制，且以预制为准，不以场景中的Override为准；
    /// 3. 数据直接从场景中获取，非最小单位的对象，无论是预制还是非预制对象，预制如果有Override，以场景中的Override为准；
    /// 4. GPU类型的资源，需要导出的对象必须要挂载LODGroup组件（无极缩放需要），预制资源则无要求；
    /// 5. 节点结构有规范要求；
    /// 6. 支持各种装饰物类型（地形、山脉、树木、河流、建筑等）；
    /// </summary>
    public class SceneStaticDataExportWindow : EditorWindow
    {
        // 简单场景
        private static readonly string[] SceneList =
        {
            "Assets/Demo/WorldMap/SimpleRes/ArtScene_1.unity",
            "Assets/Demo/WorldMap/SimpleRes/ArtScene_2.unity",
        };
        private const string ConfigPath = "Assets/Demo/WorldMap/SimpleRes/ResData";
        private const string StreamPath = "Assets/Demo/WorldMap/SimpleRes/ResStream";
        
        // 复杂场景
        // private static readonly string[] SceneList =
        // {
        //     "Assets/Demo/WorldMap/Scene/ArtScene_1.unity",
        //     "Assets/Demo/WorldMap/Scene/ArtScene_2.unity",
        // };
        // private const string ConfigPath = "Assets/Demo/WorldMap/ResData";
        // private const string StreamPath = "Assets/Demo/WorldMap/ResStream";
        
        private const string PrefabConfigPath = ConfigPath + "/SceneStaticPrefab.asset";
        private const string GPUConfigPath = ConfigPath + "/SceneStaticGPU.asset";
        private const string DataConfigPath = ConfigPath + "/SceneStaticData.asset";
        private const string LODConfigPath = ConfigPath + "/SceneStaticLOD.asset";
        private const string TransConfigPath = StreamPath + "/SceneStaticTransform.bytes";
        
        private const float ClusterMaxSize = 32;
        private const int ClusterMaxCount = 32;
        
        private const float SplitChunkX = 0;
        private const float SplitChunkZ = 0;
        
        [MenuItem("Demo/WorldMap/场景静态渲染数据导出")]
        public static async void Export()
        {
            if (!EditorUtility.DisplayDialog("提示", "确定导出场景静态渲染数据？", "确定", "取消"))
            {
                return;
            }
            
            var prefabMap = new Dictionary<string, List<GameObject>>();
            var gpuMap = new Dictionary<string, List<GameObject>>();
            foreach (var scenePath in SceneList)
            {
                var scene = await UnitySceneManager.instance.OpenAsync(scenePath);
                CollectScene(scene, prefabMap, gpuMap);
            }
            CreatePrefabConfig(prefabMap, PrefabConfigPath);
            var transList = new List<float4x4>();
            CreateGPUConfig(gpuMap, GPUConfigPath, transList);
            CreateTransConfig(transList, TransConfigPath);
            // 关闭场景
            foreach (var scenePath in SceneList)
            {
                UnitySceneManager.instance.Close(scenePath);
            }
            AssetDatabase.Refresh();
            // 创建设置资源
            var data = ScriptableObject.CreateInstance<StaticRenderData>();
            data.gpu = AssetDatabase.LoadAssetAtPath<GPUStaticRenderData>(GPUConfigPath);
            data.prefab = AssetDatabase.LoadAssetAtPath<PrefabStaticRenderData>(PrefabConfigPath);
            data.dataFileName = Path.GetFileNameWithoutExtension(TransConfigPath);
            EditorUtil.CreateDirByFilePath(DataConfigPath);
            AssetDatabase.CreateAsset(data, DataConfigPath);
            // LOD 设置资源是手调资源，仅创建不覆盖
            if (!File.Exists(LODConfigPath))
            {
                var lod = ScriptableObject.CreateInstance<StaticRenderLOD>();
                EditorUtil.CreateDirByFilePath(LODConfigPath);
                AssetDatabase.CreateAsset(lod, LODConfigPath);
            }
            // 在场景中创建好可以直接运行的挂载了SceneStaticDataRenderer组件的GO
            var go = new GameObject("SceneStaticDataRenderer");
            var renderer = go.AddComponent<SceneStaticDataRenderer>();
            renderer.data = AssetDatabase.LoadAssetAtPath<StaticRenderData>(DataConfigPath);
            renderer.lodSetting = AssetDatabase.LoadAssetAtPath<StaticRenderLOD>(LODConfigPath);
            EditorUtility.SetDirty(renderer);

            EditorUtility.DisplayDialog("提示", "导出完毕！", "确定");
        }

        private static void CollectScene(Scene scene, Dictionary<string, List<GameObject>> prefabMap, Dictionary<string, List<GameObject>> gpuMap)
        {
            var json = @"
{
    ""(?i)(Prefab)(?-i)"": {
        ""(.+)"": {
            "".+"": 0
        }
    },
    ""(?i)(GPU)(?-i)"": {
        ""(.+)"": {
            "".+"": 0
        }
    }
}
";
            var pickup = new HierarchyGameObjectPickup(json, true, true);
            pickup.onPickup = (gameObject, value, groups) =>
            {
                var isGPU = groups[0].ToLower().Equals("gpu");
                var type = groups[1].ToLower();
                var goMap = isGPU ? gpuMap : prefabMap;
                if (!goMap.ContainsKey(type))
                {
                    goMap.Add(type, new List<GameObject>());
                }
                var goList = goMap[type];
                if (isGPU)
                {
                    var lods = gameObject.GetComponentsInChildren<LODGroup>();
                    foreach (var lodGroup in lods)
                    {
                        var go = lodGroup.gameObject;
                        if (PrefabUtility.IsAnyPrefabInstanceRoot(go))
                        {
                            goList.Add(go);
                        }
                    }
                }
                else
                {
                    if (PrefabUtility.IsAnyPrefabInstanceRoot(gameObject))
                    {
                        goList.Add(gameObject);
                    }
                }
            };
            pickup.Pickup(scene);
        }

        private static void CreatePrefabConfig(Dictionary<string, List<GameObject>> prefabMap, string exportPath)
        {
            SceneStaticDataExport.BuildSimpleBVH(prefabMap, out var prefabInfoList, out var binaryTreeMap);
            var config = SceneStaticDataExport.ExportPrefabConfig(prefabInfoList, binaryTreeMap);
            EditorUtil.CreateDirByFilePath(exportPath);
            AssetDatabase.CreateAsset(config, exportPath);
        }

        private static void CreateGPUConfig(Dictionary<string, List<GameObject>> gpuMap, string exportPath, List<float4x4> transList)
        {
            SceneStaticDataExport.BuildClusterBVH(gpuMap, ClusterMaxSize, ClusterMaxCount, SplitChunkX, SplitChunkZ, out var prefabInfoList, out var binaryTreeMap);
            var config = SceneStaticDataExport.ExportGPUConfig(prefabInfoList, binaryTreeMap, transList);
            EditorUtil.CreateDirByFilePath(exportPath);
            AssetDatabase.CreateAsset(config, exportPath);
        }

        private static void CreateTransConfig(List<float4x4> transList, string exportPath)
        {
            var bytes = transList.ToArray().Array2Bytes();
            EditorUtil.CreateDirByFilePath(exportPath);
            FilesUtil.SaveAsBytes(exportPath, bytes);
        }
    }
}
